use crate::color::Color;
use std::mem::transmute;
use std::num::NonZeroI8;

#[repr(i8)]
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum PieceType {
    // 帅将
    KING = 1,
    // 车
    CHARIOT = 2,
    // 炮
    CANNON = 3,
    // 马
    KNIGHT = 4,
    // 象
    MINISTER = 5,
    // 士
    GUARD = 6,
    // 兵卒
    PAWN = 7,
}

impl PieceType {
    pub fn human_string(self, color: Color) -> String {
        match self {
            PieceType::KING => {
                if color == Color::RED {
                    "帅"
                } else {
                    "将"
                }
            }
            PieceType::CHARIOT => "车",
            PieceType::CANNON => "炮",
            PieceType::KNIGHT => "马",
            PieceType::MINISTER => "象",
            PieceType::GUARD => "士",
            PieceType::PAWN => {
                if color == Color::RED {
                    "兵"
                } else {
                    "卒"
                }
            }
        }
        .to_string()
    }

    pub fn piece_type_index(self) -> usize {
        let index = (self as usize) - 1;
        assert!(index < 7);
        index
    }
}

pub const PIECE_TYPES: [PieceType; 7] = [
    PieceType::KING,
    PieceType::CHARIOT,
    PieceType::CANNON,
    PieceType::KNIGHT,
    PieceType::MINISTER,
    PieceType::GUARD,
    PieceType::PAWN,
];

#[derive(Clone, Copy, PartialEq, Eq)]
pub struct Piece {
    // 正负区分红黑，绝对值区分兵种
    p: NonZeroI8,
}

impl Piece {
    pub fn new(color: Color, piece_type: PieceType) -> Self {
        let p = piece_type as i8;
        // 颜色隐含正负
        let color = color as i8;
        assert!(color == 1 || color == -1);
        let p = p * color;
        assert!(p != 0);
        Piece {
            p: unsafe { NonZeroI8::new_unchecked(p) },
        }
    }

    pub fn parse_char(c: char) -> Option<Self> {
        if !c.is_ascii() {
            return None;
        }
        let color = if c.is_lowercase() {
            Color::BLACK
        } else {
            Color::RED
        };
        let p: PieceType = match c.to_ascii_lowercase() {
            'k' => PieceType::KING,
            'r' => PieceType::CHARIOT,
            'c' => PieceType::CANNON,
            'n' => PieceType::KNIGHT,
            'b' => PieceType::MINISTER,
            'a' => PieceType::GUARD,
            'p' => PieceType::PAWN,
            _ => return None,
        };
        Some(Piece::new(color, p))
    }

    pub fn color(self) -> Color {
        if self.p.get() > 0 {
            Color::RED
        } else {
            Color::BLACK
        }
    }

    pub fn piece_type(self) -> PieceType {
        let p: i8 = self.p.get();
        let abs: i8 = if p > 0 { p } else { -p };
        let piece_type: PieceType = unsafe { transmute(abs) };
        piece_type
    }
}
